package com.googlecode.mailnet.emf.netmodel.visualization;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.notation.Edge;
import org.eclipse.swt.widgets.Display;

import com.googlecode.mailnet.emf.netmodel.LinkModel;
import com.googlecode.mailnet.emf.netmodel.NodeModel;
import com.googlecode.mailnet.emf.netmodel.diagram.edit.parts.LinkModelEditPart;
import com.googlecode.mailnet.emf.netmodel.diagram.edit.parts.NetworkConfigurationModelEditPart;
import com.googlecode.mailnet.emf.netmodel.diagram.edit.parts.NodeModelEditPart;
import com.googlecode.mailnet.emf.netmodel.simulation.ISimulationListener;
import com.googlecode.mailnet.emf.netmodel.simulation.SimulationContext;
import com.googlecode.mailnet.info.ILinkInfo;
import com.googlecode.mailnet.info.INodeInfo;
import com.googlecode.mailnet.info.ISimulationInfo;

/**
 * The base class for any model decorator. Provides basic functionality of
 * decorating connections and node. Derive to implement getLinkDecorator
 * and getNodeDecorator.
 * @author gleb
 */
public abstract class ModelDecorator implements ISimulationListener {

	protected SimulationContext context = null;
	
	NetworkConfigurationModelEditPart editPart;
	
	public void apply(NetworkConfigurationModelEditPart editPart, SimulationContext context) {
		if (this.context != null) {
			this.context.removeListener(this);
		}
		this.editPart = editPart;
		this.context = context;
		context.addListener(this);
		Map<LinkModel, LinkModelEditPart> model2edit = new HashMap<LinkModel, LinkModelEditPart>();
		
		// collect edit parts
		Iterator<EditPart> epIt = editPart.getConnections().iterator();
		while (epIt.hasNext()) {
			EditPart someEditPart = epIt.next();
			if (!(someEditPart instanceof LinkModelEditPart)) {
				System.out.println("? unknown link edit part class - SKIP");
				continue;
			}
			Object edgeObject = someEditPart.getModel();
			if (!(edgeObject instanceof Edge)) {
				System.out.println("? edit part model object is not of Edge type - SKIP");
				continue;
			}
			EObject linkModelObject = ((Edge)edgeObject).getElement();
			if (!(linkModelObject instanceof LinkModel)) {
				System.out.println("? invalid model object for link model edit part - SKIP");
			} else {
				model2edit.put((LinkModel) linkModelObject, (LinkModelEditPart) someEditPart);
			}
		}
		
		ISimulationInfo info = context.getInfo();
		
		// decorate links
		Iterator<? extends ILinkInfo> it = info.getLinkInfoSet().values().iterator();
		while (it.hasNext()) {
			ILinkInfo linkInfo = it.next();
			LinkModel linkModel = context.resolveModel(linkInfo.getLink());
			LinkModelEditPart linkModelEditPart = model2edit.get(linkModel);
			linkModelEditPart.setDecorator(getLinkDecorator(linkInfo));
		}
		
		Iterator<? extends INodeInfo> n_it = info.getNodeInfoSet().values().iterator();
		while (n_it.hasNext()) {
			INodeInfo nodeInfo = n_it.next();
			NodeModel nodeModel = context.resolveModel(nodeInfo.getNode());
			EditPart nodeEditPart = editPart.findEditPart(editPart, nodeModel);
			if (nodeEditPart == null) {
				System.out.println("can't find edit part for node model");
				continue;
			}
			NodeModelEditPart nodeModelEditPart = (NodeModelEditPart) nodeEditPart;
			nodeModelEditPart.setDecorator(getNodeDecorator(nodeInfo));
		}
	}
	
	public void detach() {
		if (context != null) context.removeListener(this);
	}
	
	@Override
	public void onChanged(final SimulationContext context) {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				if (context != null) {
					 editPart.getFigure().repaint();
				}
			}
		});
	}

	public static void detach(NetworkConfigurationModelEditPart editPart) {
		Map<LinkModel, LinkModelEditPart> model2edit = new HashMap<LinkModel, LinkModelEditPart>();
		
		// collect edit parts
		Iterator<EditPart> epIt = editPart.getConnections().iterator();
		while (epIt.hasNext()) {
			EditPart someEditPart = epIt.next();
			if (someEditPart instanceof NodeModelEditPart) {
				((NodeModelEditPart) someEditPart).setDecorator(null);
			}
			if (!(someEditPart instanceof LinkModelEditPart)) {
				System.out.println("? unknown link edit part class - SKIP");
				continue;
			}
			
			Object edgeObject = someEditPart.getModel();
			if (!(edgeObject instanceof Edge)) {
				System.out.println("? edit part model object is not of Edge type - SKIP");
				continue;
			}
			EObject linkModelObject = ((Edge)edgeObject).getElement();
			if (!(linkModelObject instanceof LinkModel)) {
				System.out.println("? invalid model object for link model edit part - SKIP");
			} else {
				((LinkModelEditPart) someEditPart).setDecorator(null);
			}
		}
		epIt = editPart.getChildren().iterator();
		while (epIt.hasNext()) {
			EditPart someEditPart = epIt.next();
			if (someEditPart instanceof NodeModelEditPart) {
				((NodeModelEditPart) someEditPart).setDecorator(null);
			}
		}
	}
	
	/**
	 * Returns the link decorator for the given link state info object.
	 * @param linkInfo The link inforamtion object.
	 * @return Link decorator object.
	 */
	public abstract ShapeDecorator getLinkDecorator(ILinkInfo linkInfo);
	
	/**
	 * Returns the node decorator for the given node state info.
	 * @param nodenfo The node info object.
	 * @return Decorator object.
	 */
	public abstract ShapeDecorator getNodeDecorator(INodeInfo linkInfo);
}